package com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl;

import java.util.*;

import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSONObject;
import com.iwhalecloud.citybrain.flow.platform.manager.application.ConvertUtil.FormProcessInstanceHandleRelConvertUtil;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.command.StartProcessInstanceCmd;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.threadlocal.CommonOperateLogHolder;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.CallbackThirdPlatformAfterTaskCreatedService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.FlowFormService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl.task.SyncProcessTaskProcessor;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ConstanceVariable;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ErrorCode;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpCommandDto;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpCommandResultDto;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpStartProcessInstanceDto;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.enums.ReadOperateTypeEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowFormExternalInstanceDO;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowProcessInstanceTraceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.form.FormFieldValidateRequestVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.form.FormFieldValidateResultVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.form.FormInstanceDto;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.form.FormProcessInstanceHandleRelDto;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.*;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.FormService;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;

/**
 * @author lilh
 * @date 2019-02-19 22:45
 */
@Service
public class StartProcessInstanceCommandHandler extends AbstractCommandHandler {

    @Autowired
    private ManagementService managementService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private CallbackThirdPlatformAfterTaskCreatedService callbackThirdPlatformAfterTaskCreatedService;

    @Autowired
    private TaskExecutor taskExecutor;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private FlowFormInstanceRepository formInstanceRepository;

    @Autowired
    private FlowProcessInstanceTraceRepository flowProcessInstanceTraceRepository;

    @Autowired
    private FormService formService;

    @Autowired
    private FlowFormRepository flowFormRepository;

    @Autowired
    private FlowFormExternalRepository flowFormExternalRepository;

    @Autowired
    private FlowProcessInstanceHandleRelRepository handleRelRepository;

    @Autowired
    private FlowFormService flowFormService;

    @Autowired
    private SyncProcessTaskProcessor syncProcessTaskProcessor;

    /**
     * 阅办任务跳转地址
     */
    @Value("${task.readDoJumpUrl:http://flow-platform-web-daily.ingress.dayu.work/todo/templatepage?actype=todo&processInstanceId=:processInstanceId&taskId=:taskId}")
    private String readDoJumpUrl;

    /**
     * 任务来源应用编码
     */
    @Value("${task.sourceAppCode:mes2}")
    private String sourceAppCode;


    @Override
    public String handleType() {
        return START_PROCESS_INSTANCE;
    }

    @Override
    public HttpCommandResultDto handle(String params) {
        HttpStartProcessInstanceDto startProcessInstanceDto = JSONObject.parseObject(params, HttpStartProcessInstanceDto.class);
        return handle(startProcessInstanceDto);
    }

    @Override
    public HttpCommandResultDto handle(HttpCommandDto commandDto) {
        HttpStartProcessInstanceDto startProcessInstanceDto = (HttpStartProcessInstanceDto) commandDto;
        CommonOperateLogHolder.getContent().setRequest(startProcessInstanceDto);
        //判断流程已经启动了
        if (isStarted(startProcessInstanceDto)) {
            return resolveFailResult(startProcessInstanceDto.getProcessInstanceId(), ErrorCode.PROCESS_INSTANCE_ALREADY_STARTED.errMessage());
        }

        FormFieldValidateRequestVo validateRequest = new FormFieldValidateRequestVo();

        validateRequest.setProcessInstanceId(startProcessInstanceDto.getProcessInstanceId());
        validateRequest.setFlowParamList(startProcessInstanceDto.getFlowParamList());
        validateRequest.setFlowPassList(startProcessInstanceDto.getFlowPassList());
        FormFieldValidateResultVo resultVo =
            flowFormService.validateFormFieldLimits(validateRequest);

        if (!resultVo.isPass()) {
            return resolveResult(resultVo.getMessage(), null);
        }

        ProcessInstance processInstance = managementService.executeCommand(new StartProcessInstanceCmd(startProcessInstanceDto));
        ProcessDefinition processDefinitionObj = saveStartFormDataIfExists(processInstance, startProcessInstanceDto);

        // 保存流程实例中处理关系信息
        this.saveFormProcessInstanceHandleRel(processInstance, startProcessInstanceDto, processDefinitionObj);

        //这个节点，创建的任务数据已经入库，可查询已生成的任务，回调通知第三方系统，可异步操作
        taskExecutor.execute(new CallbackRunner(processInstance));

        //保存外置表单实例
        String processDefinitionId = processInstance.getProcessDefinitionId();

        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
        if (processDefinition.hasStartFormKey()){
            String formKey = formService.getStartFormKey(processDefinition.getId());
            if (StringUtils.isNotBlank(formKey)){
                String formId = flowFormRepository.findFormIdByFormKey(formKey);

                boolean isExternalForm = flowFormExternalRepository.isExternalForm(formId);
                if (isExternalForm){
                    FlowFormExternalInstanceDO flowFormExternalInstanceDO = new FlowFormExternalInstanceDO();
                    flowFormExternalInstanceDO.setFormId(formId);
                    flowFormExternalInstanceDO.setFormKey(formKey);
                    flowFormExternalInstanceDO.setProcessInstanceId(processInstance.getProcessInstanceId());
                    flowFormExternalRepository.save(flowFormExternalInstanceDO);
                }
            }
        }


        Map<String,Object> flowParam = startProcessInstanceDto.getFlowPassList();
        if (MapUtils.isNotEmpty(flowParam)){
            Object urgentLevelObj = flowParam.get(ConstanceVariable.URGENT_LEVEL);
            if (Objects.nonNull(urgentLevelObj)){
                int urgentLevel = (int) urgentLevelObj;
                FlowProcessInstanceTraceVo flowProcessInstanceTraceVo = flowProcessInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(processInstance.getProcessInstanceId());
                flowProcessInstanceTraceVo.setUrgentLevel(urgentLevel);
                List<FlowProcessInstanceTraceVo> flowProcessInstanceTraceVoList = new ArrayList<>();
                flowProcessInstanceTraceVoList.add(flowProcessInstanceTraceVo);
                flowProcessInstanceTraceRepository.update(flowProcessInstanceTraceVoList);
                // 发起成功之后,紧急程度需要更新同步给待办组件
                syncProcessTaskProcessor.syncDataByProcess(flowProcessInstanceTraceVo.getProcessInstanceId(), urgentLevel);
            }
        }

        return resolveResult(processInstance, ErrorCode.START_PROCESS_INSTANCE_FAILED.errMessage(), START_PROCESS_INSTANCE_SUCCESS);
    }

    private ProcessDefinition saveStartFormDataIfExists(ProcessInstance processInstance, HttpStartProcessInstanceDto startProcessInstanceDto) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processInstance.getProcessDefinitionId()).singleResult();
        if (Objects.isNull(processDefinition)) {
            logger.warn("流程定义：{} 不存在", processInstance.getProcessDefinitionId());
            return null;
        }
        if(processDefinition.hasStartFormKey() && MapUtil.isNotEmpty(startProcessInstanceDto.getFlowParamObjectMap())) {
            //将表单数据写入相应表中
            formInstanceRepository.insertFormInstance(resolveFormInstance(processInstance, startProcessInstanceDto.getFlowParamObjectMap()));
        }
        return processDefinition;
    }

    private FormInstanceDto resolveFormInstance(ProcessInstance processInstance, Map<String, Object> variables) {
        FormInstanceDto formInstance = new FormInstanceDto();
        formInstance.setProcessInstanceId(processInstance.getProcessInstanceId());
        formInstance.setResource(JSONObject.parseObject(JSONObject.toJSONString(variables)));
        return formInstance;
    }

    private boolean isStarted(HttpStartProcessInstanceDto startProcessInstanceDto) {
        List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery().processInstanceId(startProcessInstanceDto.getProcessInstanceId()).list();
        return CollectionUtils.isNotEmpty(tasks);
    }

    /**
     * 保存流程实例中处理关系信息
     *
     * @param processInstance
     * @param startProcessInstanceDto
     * @param processDefinition
     */
    private void saveFormProcessInstanceHandleRel(ProcessInstance processInstance, HttpStartProcessInstanceDto startProcessInstanceDto, ProcessDefinition processDefinition) {
        // 校验流程定义对象是否为空
        if (Objects.isNull(processDefinition)) {
            logger.warn("流程定义：{} 不存在", processInstance.getProcessDefinitionId());
            return;
        }

        // 校验参数列表是否为空
        if(processDefinition.hasStartFormKey() && MapUtil.isNotEmpty(startProcessInstanceDto.getFlowParamObjectMap())) {
            // 保存流程实例与处理人(这里是阅办人)的关系信息
            try {
                Map<String, String> map = new HashMap<>();
                map.put("readDoJumpUrl", readDoJumpUrl);
                map.put("sourceAppCode", sourceAppCode);
                map.put("participantId", startProcessInstanceDto.getParticipantId());

                List<FormProcessInstanceHandleRelDto> formProcessInstanceHandleRelDtos = FormProcessInstanceHandleRelConvertUtil.convertToProcessInstanceHandleRelDto(
                        processInstance.getProcessInstanceId(),  startProcessInstanceDto.getFlowParamObjectMap(), map,
                        null, ReadOperateTypeEnum.STARTPROCESSINSTANCE.getCode());
                handleRelRepository.batchInsertFlowFormProcessInstanceHandleRel(formProcessInstanceHandleRelDtos);
            } catch (Exception ex) {
                logger.error("发起流程存储阅办人关联信息时失败,流程实例ID:" + processInstance.getProcessInstanceId() + ",失败信息：" + ex.getMessage(), ex);
            }
        }
    }

    private class CallbackRunner implements Runnable {

        private ProcessInstance processInstance;

        CallbackRunner(ProcessInstance processInstance) {
            this.processInstance = processInstance;
        }

        @Override
        public void run() {
            List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list();
            callbackThirdPlatformAfterTaskCreatedService.handle(tasks);
        }
    }
}
